﻿using System;
using Microsoft.Xna.Framework;
using ProtoGame.Input;
using ProtoGame.GameLib;
using Microsoft.Xna.Framework.Audio;
using ProtoGame.DescritoresContent;
using ProtoGame.Sprites;
using ProtoGame.RegioesGatilho;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;

namespace ProtoGame.Fases.Fantasma
{
    class PGSalasFantasma<TIdSaidas> where TIdSaidas : struct
    {
        struct DialogosDesc
        {
            public Rectangle Regiao;
            public byte Id;

            public DialogosDesc(Rectangle regiao, byte id)
            {
                this.Regiao = regiao;
                this.Id = id;
            }
        }

        struct FantasmasDesc
        {
            public Vector2 Posicao;
            public Color Cor;

            public FantasmasDesc(Vector2 posicao, Color cor)
            {
                this.Posicao = posicao;
                this.Cor = cor;
            }
        }

        struct SalaFantasmaDesc
        {
            public ushort[,] Solo;
            public Rectangle[] Impassaveis;
            public Rectangle? EscadaProximo;
            public Rectangle? EscadaEntrada;
            public FantasmasDesc Fantasma;
            public DialogosDesc[] Dialogos;
            public Vector2 JogadorInicio;
            public Vector2 PlacaMarcadora;
        }

        SalaFantasmaDesc Inicio;
        SalaFantasmaDesc Meio;
        SalaFantasmaDesc Fim;

        Game game;
        IPGEntradasAcao entradas;
        Tileset fantasmaSetGfx;
        Tileset koboldGfx;
        Tileset espadaGfx;
        Tileset fantasmaGfx;
        Texture2D placaGfx;
        SpriteFont placaFont;
        SoundEffect espadaSfx;
        SoundEffect falaSfx;
        SoundEffect atacaSfx;
        SoundEffect morteSfx;

        Stack<PGSpriteFantasma> fantasmasLigados;
        int qtdeFantasmas;

        int qtdeSalasCriadas = 0;

        public Stack<PGSpriteFantasma> FantasmasLigados
        {
            get { return fantasmasLigados; }
        }

        public PGSalasFantasma(int qtdeFantasmas, Game game)
        {
            #region Dados Inicio
            Inicio.Solo = new ushort[,] {
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27},
                {27,27,67,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,72,27,27},
                {27,27,36,63,63,64,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,33,27,27},
                {27,27,36,81,81,82,142,143,81,81,81,81,81,81,81,81,81,81,81,81,81,82,33,27,27},
                {27,27,36,99,99,100,160,161,99,99,99,99,99,124,99,99,99,99,99,99,99,100,33,27,27},
                {27,27,36,117,117,118,178,179,117,117,117,117,117,117,117,117,117,117,117,117,117,118,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,258,258,257,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,44,87,87},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,80,63,63},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,98,99,99},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,116,117,117},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,10,9,8},
                {27,27,36,115,276,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,10,9,8},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,276,115,115,115,115,15,16,16},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,49,16,16,16,16,16,16,16,16,16,16,16,16,17,18,7,7,7,15,17,50,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,217,218,218,33,27,27,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,235,236,218,33,27,27,27,27}};
            
            Inicio.Impassaveis = new Rectangle[] {
                new Rectangle(709,12,16,517),//1
                new Rectangle(642,515,69,91),//2
                new Rectangle(80,513,458,96),//3
                new Rectangle(78,33,13,495),//4
                new Rectangle(65,117,663,28)};//5
  
            Inicio.EscadaProximo = new Rectangle(493, 592, 192, 10);

            Inicio.EscadaEntrada = null;

            Inicio.Fantasma = new PGSalasFantasma<TIdSaidas>.FantasmasDesc(new Vector2(584, 331), Color.White);
            
            Inicio.Dialogos = new DialogosDesc[] {
                new DialogosDesc(new Rectangle(159, 197, 124, 63), 0)};

            Inicio.JogadorInicio = new Vector2(213, 201);

            Inicio.PlacaMarcadora = new Vector2(110, 158);

            #endregion

            #region Dados Meio
            Meio.Solo = new ushort[,] {
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27},
                {27,27,67,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,72,27,27},
                {27,27,36,63,63,64,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,33,27,27},
                {27,27,36,81,81,82,142,143,81,81,81,81,81,81,81,81,81,81,81,81,81,82,33,27,27},
                {27,27,36,99,99,100,160,161,99,99,99,99,99,124,99,99,99,99,99,99,99,100,33,27,27},
                {27,27,36,117,117,118,178,179,117,117,117,117,117,117,117,117,117,117,117,117,117,118,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,258,258,257,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,276,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,276,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,49,16,16,16,16,16,16,16,16,16,16,16,16,17,18,7,7,7,15,17,50,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,217,218,218,33,27,27,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,235,236,218,33,27,27,27,27}};

            Meio.Impassaveis = new Rectangle[] {
                new Rectangle(709, 12, 16, 517),
                new Rectangle(642, 515, 69, 91),
                new Rectangle(80, 513, 458, 96),
                new Rectangle(78, 33, 13, 495),
                new Rectangle(65, 117, 663, 28)};

            Meio.EscadaProximo = new Rectangle(493,592,192,10);

            Meio.EscadaEntrada = new Rectangle(208,154,36,16);

            Meio.Fantasma = new PGSalasFantasma<TIdSaidas>.FantasmasDesc(new Vector2(584, 331), Color.White);

            Meio.JogadorInicio = new Vector2(213, 201);

            Meio.PlacaMarcadora = new Vector2(110, 158);

            #endregion

            #region Dados Fim
            Fim.Solo = new ushort[,] {
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27},
                {27,27,67,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,72,27,27},
                {27,27,36,63,63,64,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,33,27,27},
                {27,27,36,81,81,82,142,143,81,81,81,81,81,81,81,81,81,81,81,81,81,82,33,27,27},
                {27,27,36,99,99,100,160,161,99,99,99,99,99,124,99,124,99,124,99,124,99,100,33,27,27},
                {27,27,36,117,117,118,178,179,117,117,117,117,117,117,117,117,117,117,117,117,117,118,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,258,258,257,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,36,115,166,167,168,115,115,115,115,115,115,115,115,115,115,172,173,174,115,115,33,27,27},
                {27,27,36,115,184,185,186,115,115,115,115,115,115,115,115,115,115,190,191,192,115,115,33,27,27},
                {27,27,36,115,202,203,204,115,115,115,115,115,115,115,115,115,115,208,209,210,115,115,33,27,27},
                {27,27,36,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,33,27,27},
                {27,27,49,16,16,16,16,16,16,16,16,16,16,16,16,17,18,8,9,10,15,17,50,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,8,10,10,33,27,27,27,27},
                {27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,36,10,9,8,33,27,27,27,27}};
  
            Fim.Impassaveis = new Rectangle[] {
                new Rectangle(709,12,16,517),
                new Rectangle(80,513,669,14),
                new Rectangle(78,33,13,495),
                new Rectangle(65,117,663,28)};

            Fim.EscadaProximo = null;

            Fim.EscadaEntrada = new Rectangle(208, 154, 36, 16);

            Fim.Fantasma = new PGSalasFantasma<TIdSaidas>.FantasmasDesc(new Vector2(556, 360), Color.PaleGreen);
            
            Fim.Dialogos = new DialogosDesc[] {
                new DialogosDesc(new Rectangle(160, 201, 143, 64), 1)};

            Fim.JogadorInicio = new Vector2(213, 201);

            Fim.PlacaMarcadora = new Vector2(110, 158);

            #endregion

            entradas = game.Services.GetService(typeof(IPGEntradasAcao)) as IPGEntradasAcao;
            fantasmaSetGfx = PGIMGFantasma.CriarTileset(game.Content);
            koboldGfx = PGIMGKobold.CriarTileset(game.Content);
            espadaGfx = PGIMGAtaqueEspada.CriarTileset(game.Content);
            fantasmaGfx = PGIMGSpriteFantasma.CriarTileset(game.Content);
            placaGfx = game.Content.Load<Texture2D>(@"Images/Sprites/Placa");

            placaFont = game.Content.Load<SpriteFont>(@"Fonts/Storytelling");

            espadaSfx = PGSFXAtaqueEspada.CriarSoundEffect(game.Content);
            falaSfx = PGSFXFantasma.CriarFala(game.Content);
            atacaSfx = PGSFXFantasma.CriarAtaque(game.Content);
            morteSfx = PGSFXFantasma.CriarMorre(game.Content);

            fantasmasLigados = new Stack<PGSpriteFantasma>();

            this.game = game;
            this.qtdeFantasmas = qtdeFantasmas;
        }

        EntityManager ConstruirLabirinto(SalaFantasmaDesc salaFantasma, TIdSaidas? idVoltaSala, TIdSaidas? idProximaSala, EventHandler<MudaMapaEventArgs<TIdSaidas>> mudaMapa_handler, EventHandler jogador_conversa)
        {
            const float planoFundo = 1f;
            const float planoItens = planoFundo / 2;
            const float planoAcao = planoItens / 2;

            EntityManager novaSalaFantasma = new EntityManager(game);

            Tilemap solo = new Tilemap(salaFantasma.Solo, fantasmaSetGfx);
            solo.LayerDepth = planoFundo;
            solo.CollisionType = TiposColisoes.Nenhum;
            novaSalaFantasma.AddEntity(solo);
            novaSalaFantasma.WorldBounds = solo.Bounds;

            PGAtaqueEspada ataque = PGAtaqueEspada.CriarAtaqueEspada(espadaGfx, espadaSfx);
            novaSalaFantasma.AddEntity(ataque);

            AtributosJogador atributosJogador = game.Services.GetService(typeof(AtributosJogador)) as AtributosJogador;

            PGSpriteJogador jogador = new PGSpriteJogador(atributosJogador, ataque, entradas, koboldGfx);
            jogador.Position = salaFantasma.JogadorInicio;
            jogador.LayerDepth = planoAcao;
            novaSalaFantasma.AddEntity(jogador);
            novaSalaFantasma.FrameFocus = jogador;

            foreach (Rectangle area in salaFantasma.Impassaveis)
            {
                PGAreaColisao areaImpassavel = new PGAreaColisao(area);
                areaImpassavel.CollisionType = TiposColisoes.Solido;

                novaSalaFantasma.AddEntity(areaImpassavel);
            }

            if (salaFantasma.EscadaProximo != null)
            {
                PGGatilhoMudaMapa<TIdSaidas> areaEscada = new PGGatilhoMudaMapa<TIdSaidas>((Rectangle)salaFantasma.EscadaProximo, (TIdSaidas)idProximaSala);
                areaEscada.CollisionType = TiposColisoes.Solido;
                areaEscada.Triggered += new EventHandler<MudaMapaEventArgs<TIdSaidas>>(mudaMapa_handler);

                novaSalaFantasma.AddEntity(areaEscada);
            }

            if (salaFantasma.EscadaEntrada != null)
            {
                PGGatilhoMudaMapa<TIdSaidas> areaEscadaEntrada = new PGGatilhoMudaMapa<TIdSaidas>((Rectangle)salaFantasma.EscadaEntrada, (TIdSaidas)idVoltaSala);
                areaEscadaEntrada.CollisionType = TiposColisoes.Solido;
                areaEscadaEntrada.Triggered += new EventHandler<MudaMapaEventArgs<TIdSaidas>>(mudaMapa_handler);
                novaSalaFantasma.AddEntity(areaEscadaEntrada);
            }

            //if (salaFantasma.Fantasma != null)
            {
                PGSpriteFantasma fantasma = new PGSpriteFantasma(qtdeFantasmas, falaSfx, atacaSfx, morteSfx, fantasmaGfx);
                fantasma.JogadorAlvo = jogador;
                fantasma.FantasmasLigados = fantasmasLigados;
                fantasma.Position = salaFantasma.Fantasma.Posicao;
                fantasma.ColorEffects = salaFantasma.Fantasma.Cor;
                novaSalaFantasma.AddEntity(fantasma);
                fantasmasLigados.Push(fantasma);
            }

            if (salaFantasma.Dialogos != null)
            {
                foreach (DialogosDesc dd in salaFantasma.Dialogos)
                {
                    PGGatilhoJogador gj = new PGGatilhoJogador(dd.Regiao);
                    gj.Id = dd.Id;
                    gj.CollisionType = TiposColisoes.Nenhum;
                    gj.Triggered += new EventHandler<EventArgs>(jogador_conversa);
                    novaSalaFantasma.AddEntity(gj);
                }
            }

            PGSpritePlaca placa = new PGSpritePlaca(placaFont, placaGfx);
            placa.Position = salaFantasma.PlacaMarcadora;
            placa.LayerDepth = planoItens;
            placa.CollisionType = TiposColisoes.Solido;
            placa.Numero = ++qtdeSalasCriadas;
            placa.Visible = true;
            placa.Alive = true;
            novaSalaFantasma.AddEntity(placa);

            return novaSalaFantasma;
        }

        public EntityManager ConstruirSalaFantasmaInicio(TIdSaidas idProximaSala, EventHandler<MudaMapaEventArgs<TIdSaidas>> mudaMapa_handler, EventHandler jogador_conversa)
        {
            return ConstruirLabirinto(Inicio, null, idProximaSala, mudaMapa_handler, jogador_conversa);
        }

        public EntityManager ConstruirSalaFantasmaMeio(TIdSaidas idVoltaSala, TIdSaidas idProximaSala, EventHandler<MudaMapaEventArgs<TIdSaidas>> mudaMapa_handler, EventHandler jogador_conversa)
        {
            return ConstruirLabirinto(Meio, idVoltaSala, idProximaSala, mudaMapa_handler, jogador_conversa);
        }

        public EntityManager ConstruirSalaFantasmaFim(TIdSaidas idVoltaSala, EventHandler<MudaMapaEventArgs<TIdSaidas>> mudaMapa_handler, EventHandler jogador_conversa)
        {
            return ConstruirLabirinto(Fim, idVoltaSala, null, mudaMapa_handler, jogador_conversa);
        }
    }
}
